﻿#include "StraightLineGraphicsItem.h"
#include "GraphicsDef.h"
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QFocusEvent>

namespace OfUI {

	StraightLineGraphicsItem::StraightLineGraphicsItem(QGraphicsItem *parent)
		: BaseGraphicsItem(parent)
		, m_bAdding(true)
		, m_eSelType(SELECT_NONE)
	{
		setType(ItemType::STRAIGHT_LINE);
	}

	StraightLineGraphicsItem::~StraightLineGraphicsItem()
	{
	}

	void StraightLineGraphicsItem::setLine(const QLineF& line)
	{
		prepareGeometryChange();
		m_line = line;
		emit signalDataChanged();
	}

	QLineF StraightLineGraphicsItem::getLine() const
	{
		return m_line;
	}

	bool StraightLineGraphicsItem::isAdding() const
	{
		return m_bAdding;
	}

	bool StraightLineGraphicsItem::isEditing() const
	{
		return SELECT_NONE != m_eSelType;
	}

	bool StraightLineGraphicsItem::isVaild() const
	{
		return m_line.length() > 1e-6;
	}

	BaseGraphicsItem::MouseDragType StraightLineGraphicsItem::select(const QPointF& point, int* selType /*= nullptr */) const
	{
		MouseDragType eDragType = DRAG_NONE;
		SelectType eSelectType = SELECT_NONE;
		double dDistance = 5 / getViewScale();

		if (QLineF(m_line.p1(), point).length() <= dDistance)
		{
			eDragType = DRAG_EDIT;
			eSelectType = SELECT_P1;
		}
		else if (QLineF(m_line.p2(), point).length() <= dDistance)
		{
			eDragType = DRAG_EDIT;
			eSelectType = SELECT_P2;
		}
		else
		{
			if (!m_line.isNull() && distanceFromPointToLine(point, m_line) <= dDistance)
			{
				eDragType = DRAG_EDIT;
				eSelectType = SELECT_ALL;
			}
		}

		if (selType)
		{
			*selType = eSelectType;
		}
		return eDragType;
	}

	void StraightLineGraphicsItem::cursor(const QPointF& point, QCursor& cursor) const
	{
		int nType;
		if (BaseGraphicsItem::DRAG_NONE != select(point, &nType))
		{
			switch (nType)
			{
			case SELECT_P1:
			case SELECT_P2:
				cursor = Qt::PointingHandCursor;
				break;
			case SELECT_ALL:
				cursor = Qt::SizeAllCursor;
				break;
			default:
				break;
			}
		}
	}

	void StraightLineGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
	{
		if (Qt::LeftButton == event->button())
		{
			m_pressMousePos = event->scenePos();

			if (isAdding())
			{
				setLine(QLineF(m_pressMousePos, m_pressMousePos));
				m_eSelType = SELECT_P2;
				return;
			}
			else
			{
				int nType = SELECT_NONE;
				if (BaseGraphicsItem::DRAG_NONE != select(m_pressMousePos, &nType))
				{
					m_eSelType = static_cast<SelectType>(nType);
					return;
				}
			}
		}
		QFocusEvent focusEvent(QEvent::FocusOut, Qt::OtherFocusReason);
		focusOutEvent(&focusEvent);
	}

	void StraightLineGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
	{
		QFocusEvent focusEvent(QEvent::FocusOut, Qt::OtherFocusReason);
		focusOutEvent(&focusEvent);
	}

	void StraightLineGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
	{
		if (SELECT_NONE == m_eSelType)
			return;

		QPointF mousePos = event->scenePos();
		QRectF sceneRect = scene()->sceneRect();
		// 保证鼠标在场景内
		mousePos.setX(qMin(mousePos.rx(), sceneRect.right()));
		mousePos.setY(qMin(mousePos.ry(), sceneRect.bottom()));
		mousePos.setX(qMax(mousePos.rx(), sceneRect.left()));
		mousePos.setY(qMax(mousePos.ry(), sceneRect.top()));

		if (SELECT_P1 == m_eSelType)
		{
			if (Qt::ShiftModifier & event->modifiers())
			{
				QLineF line = getStraightLineFromObliqueLine(QLineF(m_line.p2(), mousePos));
				setLine(QLineF(line.p2(), m_line.p2()));
			}
			else
			{
				setLine(QLineF(mousePos, m_line.p2()));
			}

		}
		else if (SELECT_P2 == m_eSelType)
		{
			if (Qt::ShiftModifier & event->modifiers())
			{
				QLineF line = getStraightLineFromObliqueLine(QLineF(m_line.p1(), mousePos));
				setLine(QLineF(m_line.p1(), line.p2()));
			}
			else
			{
				setLine(QLineF(m_line.p1(), mousePos));
			}
		}
		else if (SELECT_ALL == m_eSelType)
		{
			setLine(m_line.translated(mousePos - m_pressMousePos));
			m_pressMousePos = mousePos;
		}
	}

	void StraightLineGraphicsItem::focusOutEvent(QFocusEvent *event)
	{
		Q_UNUSED(event);
		if (isAdding())
		{
			emit signalAddFinished();
			m_bAdding = false;
		}
		m_eSelType = SELECT_NONE;
	}

	QRectF StraightLineGraphicsItem::boundingRect() const
	{
		QPainterPath path;
		QPointF sPoint = m_line.p1();
		QPointF ePoint = m_line.p2();
		// 直线
		path.moveTo(sPoint);
		path.lineTo(ePoint);
		// 拖拽点
		if (sPoint != ePoint)
		{
			double dEdgeLen = 20 / getViewScale();
			double dDistance = QLineF(sPoint, ePoint).length();
			double p1x = sPoint.rx() + dEdgeLen * (ePoint.ry() - sPoint.ry()) / dDistance / 2;
			double p1y = sPoint.ry() - dEdgeLen * (ePoint.rx() - sPoint.rx()) / dDistance / 2;
			double p2x = sPoint.rx() - dEdgeLen * (ePoint.ry() - sPoint.ry()) / dDistance / 2;
			double p2y = sPoint.ry() + dEdgeLen * (ePoint.rx() - sPoint.rx()) / dDistance / 2;
			double p3y = p1y + ePoint.ry() - sPoint.ry();
			double p3x = p1x + ePoint.rx() - sPoint.rx();
			double p4y = p2y + ePoint.ry() - sPoint.ry();
			double p4x = p2x + ePoint.rx() - sPoint.rx();
			path.moveTo(sPoint);
			path.lineTo(ePoint);
			path.moveTo(QPointF(p1x, p1y));
			path.lineTo(QPointF(p2x, p2y));
			path.moveTo(QPointF(p3x, p3y));
			path.lineTo(QPointF(p4x, p4y));
		}
		QRectF rectF = path.controlPointRect().adjusted(-5, -5, 5, 5);
		return rectF;
	}

	void StraightLineGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget /* = nullptr */)
	{
		Q_UNUSED(option);
		Q_UNUSED(widget);

		if (this->scene())
		{
			painter->save();
			painter->setRenderHint(QPainter::Antialiasing);

			// 通过反向缩放painter实现矢量绘制效果
			double dViewScale = getViewScale();
			painter->scale(1 / dViewScale, 1 / dViewScale);

			// painter缩小，则绘制坐标放大，保证显示位置不变
			QPointF sPoint = m_line.p1() * dViewScale;
			QPointF ePoint = m_line.p2() *dViewScale;

			double dEdgeLen = 20;
			double dDistance = QLineF(sPoint, ePoint).length();

			double p1x = sPoint.rx() + dEdgeLen * (ePoint.ry() - sPoint.ry()) / dDistance / 2;
			double p1y = sPoint.ry() - dEdgeLen * (ePoint.rx() - sPoint.rx()) / dDistance / 2;
			double p2x = sPoint.rx() - dEdgeLen * (ePoint.ry() - sPoint.ry()) / dDistance / 2;
			double p2y = sPoint.ry() + dEdgeLen * (ePoint.rx() - sPoint.rx()) / dDistance / 2;
			double p3y = p1y + ePoint.ry() - sPoint.ry();
			double p3x = p1x + ePoint.rx() - sPoint.rx();
			double p4y = p2y + ePoint.ry() - sPoint.ry();
			double p4x = p2x + ePoint.rx() - sPoint.rx();

			// 绘制直线
			painter->setPen(getPen(getState()));
			painter->drawLine(sPoint, ePoint);
			painter->drawLine(QPointF(p1x, p1y), QPointF(p2x, p2y));
			painter->drawLine(QPointF(p3x, p3y), QPointF(p4x, p4y));

			// 绘制拖拽点
			if (STATE_SELECT == getState())
			{
				double dRadius = 2;
				painter->drawEllipse(sPoint, dRadius, dRadius);
				painter->drawEllipse(ePoint, dRadius, dRadius);
			}
			painter->restore();
		}
	}

}
